/** 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package it.free.i.virtual.machine.hal.devicealias.laboratory.actuators.drivers;

import java.util.List;
import java.util.Properties;

import it.free.i.virtual.machine.hal.DeviceAliasRuntimeException;
import it.free.i.virtual.machine.hal.LinkingOperationException;
import it.free.i.virtual.machine.hal.devicealias.DeviceAliasCommandExecutionException;
import it.free.i.virtual.machine.hal.devicealias.command.DeviceAliasPortCommand;
import it.free.i.virtual.machine.hal.devicealias.command.DeviceAliasPortCommandImpl;
import it.free.i.virtual.machine.hal.devicealias.command.DeviceAliasPortCommandVerb;
import it.free.i.virtual.machine.hal.devicealias.controller.DeviceAliasPortController;
import it.free.i.virtual.machine.hal.devicealias.port.DeviceAliasPortSupportImpl;
import it.free.i.virtual.machine.hal.devicealias.echo.ActuatorInvocationFeedBackEcho;
import it.free.i.virtual.machine.hal.devicealias.echo.ActuatorInvocationFeedBackEchoImpl;

import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.InvalidAttributeValueException;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.Notification;
import javax.management.ReflectionException;

/**
 * Do not call this from runtime routines, actuators need abstraction via 
 * EffectUnawareActuatorFireAbleCommandPrototypesCatalog instead
 * Provides the methods to allow driving the vehicle, see this class as the vehicle 
 * dashboard: steering wheel, brake, throttle etc etc
 * @author stefano
 */
public class VehicleDriver extends DeviceAliasPortSupportImpl {

	private static long sequenceNumber = 0;
	
	private static VehicleDriver vehicleDriver = new VehicleDriver();
	
	/**
	 * Keep it hidden
	 */
	private VehicleDriver(){
	}
	
	/**
	 * Single instance accessor, not like an usual singleton
	 * @return the only instance of the vehicle driver
	 */
	public static VehicleDriver getInstance(){
		return VehicleDriver.vehicleDriver;
	}
	
	/**
	 * Increase velocity of the vehicle driven by the system
	 * @throws DeviceAliasCommandExecutionException
	 */
	public final void doDriveAccelerate() throws DeviceAliasCommandExecutionException{
		
		DeviceAliasPortCommand deviceAliasPortCommandAccelerate = new DeviceAliasPortCommandImpl();    
		deviceAliasPortCommandAccelerate.setVerb(DeviceAliasPortCommandVerb.PORT_DRIVE_ACCELERATE);
			
		DeviceAliasPortController.getInstance().fireCommand(deviceAliasPortCommandAccelerate);
		
		fireFeedbackNotifications(DeviceAliasPortCommandVerb.PORT_DRIVE_ACCELERATE_FEEDBACK);
	}
	
	/**
	 * Decrease velocity of the vehicle driven by the system
	 * @throws DeviceAliasCommandExecutionException
	 */
	public final void doDriveBrake() throws DeviceAliasCommandExecutionException{
		
		DeviceAliasPortCommand deviceAliasPortCommandBrake = new DeviceAliasPortCommandImpl();    
		deviceAliasPortCommandBrake.setVerb(DeviceAliasPortCommandVerb.PORT_DRIVE_BRAKE);
			
		DeviceAliasPortController.getInstance().fireCommand(deviceAliasPortCommandBrake);
		fireFeedbackNotifications(DeviceAliasPortCommandVerb.PORT_DRIVE_BRAKE_FEEDBACK);
	}
	
	/**
	 * Cause the vehicle driven by the system to steer on the right
	 * @throws DeviceAliasCommandExecutionException
	 */
	public final void doDriveRight() throws DeviceAliasCommandExecutionException{
		
		DeviceAliasPortCommand deviceAliasPortCommandRight = new DeviceAliasPortCommandImpl();    
		deviceAliasPortCommandRight.setVerb(DeviceAliasPortCommandVerb.PORT_DRIVE_RIGHT);
			
		DeviceAliasPortController.getInstance().fireCommand(deviceAliasPortCommandRight);
		fireFeedbackNotifications(DeviceAliasPortCommandVerb.PORT_DRIVE_RIGHT_FEEDBACK);
	}
	
	/**
	 * Cause the vehicle driven by the system to steer on the left
	 * @throws DeviceAliasCommandExecutionException
	 */
	public final void doDriveLeft() throws DeviceAliasCommandExecutionException{
		
		DeviceAliasPortCommand deviceAliasPortCommandLeft = new DeviceAliasPortCommandImpl();    
		deviceAliasPortCommandLeft.setVerb(DeviceAliasPortCommandVerb.PORT_DRIVE_LEFT);
			
		DeviceAliasPortController.getInstance().fireCommand(deviceAliasPortCommandLeft);
		fireFeedbackNotifications(DeviceAliasPortCommandVerb.PORT_DRIVE_LEFT_FEEDBACK);
	}
	
	/**
	 * Upon every actuator's invocation, this method will be used to send back a notification to say that 
	 * operation was performed
	 */
	private final void fireFeedbackNotifications(DeviceAliasPortCommandVerb deviceAliasPortCommandVerb){
		
		ActuatorInvocationFeedBackEcho actuatorInvocationFeedBackEcho = new ActuatorInvocationFeedBackEchoImpl();
		
		if(deviceAliasPortCommandVerb == DeviceAliasPortCommandVerb.PORT_DRIVE_ACCELERATE_FEEDBACK){
		
			actuatorInvocationFeedBackEcho.setInvokedActuatorName("THROTTLE"); //To increase vehicle velocity
			actuatorInvocationFeedBackEcho.setValueWrittenOnActuator(0);
		}else if(deviceAliasPortCommandVerb == DeviceAliasPortCommandVerb.PORT_DRIVE_RIGHT_FEEDBACK){
			
			     actuatorInvocationFeedBackEcho.setInvokedActuatorName("DRIVE_RIGHT");
			     actuatorInvocationFeedBackEcho.setValueWrittenOnActuator(0);
		}else if(deviceAliasPortCommandVerb == DeviceAliasPortCommandVerb.PORT_DRIVE_LEFT_FEEDBACK){
			
			     actuatorInvocationFeedBackEcho.setInvokedActuatorName("DRIVE_LEFT");
			     actuatorInvocationFeedBackEcho.setValueWrittenOnActuator(0);
		}else if(deviceAliasPortCommandVerb == DeviceAliasPortCommandVerb.PORT_DRIVE_BRAKE_FEEDBACK){
			
			     actuatorInvocationFeedBackEcho.setInvokedActuatorName("BRAKE");
			     actuatorInvocationFeedBackEcho.setValueWrittenOnActuator(0);
		}
		
		
		Notification notification = super.buildNotification(deviceAliasPortCommandVerb, 
															this.getName(), 
															sequenceNumber++,
															actuatorInvocationFeedBackEcho);

		super.sendNotification(notification);
	}
	@Override
	public Object getAttribute(String attribute)
			throws AttributeNotFoundException, MBeanException,
			ReflectionException {
		return null;
	}
	@Override
	public AttributeList getAttributes(String[] attributes) {
		return null;
	}
	@Override
	public MBeanInfo getMBeanInfo() {
		return null;
	}
	@Override
	public DeviceAliasPortCommandVerb getMode() {
		return null;
	}
	@Override
	public String getName() {
		return null;
	}
	@Override
	public void handleNotification(Notification notification, Object handback) {
	}
	@Override
	public Object invoke(List<Object> input) {
		return null;
	}
	@Override
	public Object invoke(String actionName, Object[] params, String[] signature)
			throws MBeanException, ReflectionException {
		return null;
	}
	@Override
	public void link(Properties virtualDeviceConfiguration)
			throws LinkingOperationException {
	}
	@Override
	public void run() {
	}
	@Override
	public void setAttribute(Attribute attribute)
			throws AttributeNotFoundException, InvalidAttributeValueException,
			MBeanException, ReflectionException {
	}
	@Override
	public AttributeList setAttributes(AttributeList attributes) {
		return null;
	}
	@Override
	public void setMode(DeviceAliasPortCommandVerb mode) {
	}
	@Override
	public void unlink() throws LinkingOperationException {
	}
	@Override
	public void reset() throws DeviceAliasRuntimeException {
	}
	
}
